#include <jni.h>
#include <string>


#if __ANDROID_API__ >= 24
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#elif __ANDROID_API__ >= 21

#include <GLES3/gl31.h>
#include <GLES3/gl3ext.h>

#else
#include <GLES3/gl3.h>
#endif

#include "log.h"
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <time.h>
#include <assert.h>

// Vertex Shaders
const GLchar *vertexShaderSource = "#version 300 es\n"
        "layout(location = 0) in vec3 position;\n"
        "layout (location = 1) in vec3 color;\n"
        "out vec4 vertexColor;\n"
        "void main()\n"
        "{\n"
        "vertexColor = vec4(color, 1.0f);\n"
        "gl_Position = vec4(position, 1.0);\n"
        "}";

//Fragment Shader
const GLchar *fragmentShaderSource = "#version 300 es\n"
        "out vec4 color; \n"
        "uniform vec4 globalColor;\n"
        "in vec4 vertexColor;\n"
        "void main(){ \n"
        "color = vertexColor; \n"
        "}";


struct Helper{
    JavaVM *VM;
};


struct Helper helper;


GLuint VBO; //顶点坐标缓冲对象

GLuint VAO;//Vertex参数数组对象

GLuint EBO; //顶点索引对象

GLuint shaderProgram; //着色器程序


GLfloat timeColor() {
    time_t t;

    t = time(&t);

    struct tm *tm = localtime(&t);

    return sin(tm->tm_min) / 2 + 0.5f;

}


#define ARRTRI_ARRAY_VERTEXS 0
#define ARRTRI_ARRAY_COLORS 1
#define ARRTRI_ARRAY_TEXTURE 2



JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{

    helper.VM = vm;

    assert(helper.VM);

    LOGI("Saved JavaVM and JNIEnv");

    return JNI_VERSION_1_6;
}

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_oxy_nativegles_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}



extern "C"
JNIEXPORT jint JNICALL
Java_com_oxy_nativegles_MainActivity_render(JNIEnv *env, jobject instance) {


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

    glClear(GL_COLOR_BUFFER_BIT);

    GLint globalColorLocation = glGetUniformLocation(shaderProgram, "globalColor");

    glUseProgram(shaderProgram);

    glUniform4f(globalColorLocation, 0.0f, timeColor(), 0.0f, 1.0f);

    glBindVertexArray(VAO);

//    glDrawArrays(GL_TRIANGLES, 0, 3);//绘制三角形

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

//    glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 0);

    glBindVertexArray(0);

    LOGI("---------------------->渲染结束");

    return 0;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_oxy_nativegles_MainActivity_initData(JNIEnv *env, jobject instance) {


    GLfloat vertices[] = {
            //     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
            0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
            0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
            -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
            -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
    };


    GLuint indices[] = { // 注意索引从0开始!
            0, 1, 3,  // First Triangle
            1, 2, 3   // Second Triangle
    };

    glGenBuffers(1, &EBO);

    glGenVertexArrays(1, &VAO);

    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(ARRTRI_ARRAY_VERTEXS, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid *) 0);

    glEnableVertexAttribArray(ARRTRI_ARRAY_VERTEXS);

    glVertexAttribPointer(ARRTRI_ARRAY_COLORS, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid *) (3*sizeof(GLfloat)));

    glEnableVertexAttribArray(ARRTRI_ARRAY_COLORS);

    glVertexAttribPointer(ARRTRI_ARRAY_TEXTURE, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid *) (6*sizeof(GLfloat)));

    glEnableVertexAttribArray(ARRTRI_ARRAY_TEXTURE);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

}


extern "C"
JNIEXPORT void JNICALL
Java_com_oxy_nativegles_MainActivity_rsize(JNIEnv *env, jobject instance, jint width, jint height) {

    glViewport(0, 0, width, height);

}



extern "C"
JNIEXPORT void JNICALL
Java_com_oxy_nativegles_MainActivity_initShader(JNIEnv *env, jobject instance) {

    GLuint vertexShader;

    vertexShader = glCreateShader(GL_VERTEX_SHADER);

    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);

    glCompileShader(vertexShader);

    GLint success;

    GLchar infoLog[512];

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);

        LOGE("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n %s", infoLog);
    }


    GLuint fragmentShader;

    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);

    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);

    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);

        LOGE("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n %s", infoLog);

    }

    shaderProgram = glCreateProgram();

    glAttachShader(shaderProgram, vertexShader);

    glAttachShader(shaderProgram, fragmentShader);

    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);

        LOGE("ERROR::SHADER_PROGRAM::LINK_FAILED\n %s", infoLog);

    }

    glDeleteShader(vertexShader);

    glDeleteShader(fragmentShader);

}



extern "C"
JNIEXPORT void JNICALL
Java_com_oxy_nativegles_MainActivity_destroy(JNIEnv *env, jobject instance) {


    glDeleteVertexArrays(1, &VAO);

    glDeleteBuffers(1, &VBO);

    glDeleteBuffers(1, &EBO);

    glDeleteProgram(shaderProgram);

    free((void *) vertexShaderSource);

    free((void *) fragmentShaderSource);

}